package com.duguo.dynamicmvc.factory.processor.model;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;

import com.duguo.dynamicmvc.factory.ControllerBuilder;
import com.duguo.dynamicmvc.factory.ControllerFactory;
import com.duguo.dynamicmvc.factory.processor.AbstractProcessor;
import com.duguo.dynamicmvc.factory.processor.controller.ControllerNameBuilder;
import com.duguo.dynamicmvc.factory.utils.BeanUtils;
import com.duguo.dynamicmvc.factory.utils.ChangeOnceBoolean;
import com.duguo.dynamicmvc.model.ModelImpl;

public class ModelProcessorImpl extends AbstractProcessor implements ModelProcessor{
	
	ModelBinder _modelBinder;
	ModelAnnotationReader _modelAnnotationReader;
	ModelFieldCreator _modelFieldCreator;
	ModelMethodCreator _modelMethodCreator;
	ControllerNameBuilder _controllerNameBuilder;
	 

	public void addModelBindCode(CtClass controllerCtClass, StringBuffer methodBody, CtClass modelCtClass, CtMethod controllerMethod, String returnText, ChangeOnceBoolean hasValidationException,ControllerBuilder controllerBuilder) throws Exception{
		_modelBinder.addModelBindCode(controllerCtClass, methodBody, modelCtClass, controllerMethod, returnText, hasValidationException,controllerBuilder);
	}

	public void createBindMethod(ControllerFactory controllerFactory, CtClass modelImplCtClass, Map<String, CtClass> getters, Map<String, CtClass> setters, CtMethod controllerMethod, Map<String,Object> staticObjects) throws Exception{
		_modelBinder.createBindMethod(controllerFactory, modelImplCtClass, getters, setters, controllerMethod, staticObjects,_modelAnnotationReader,_modelFieldCreator);
	}

	
	public void makeDynamicViewModelImpl(ControllerFactory controllerFactory,Class modelCtClass, CtMethod controllerHandlerCtMethod) throws Exception {
		String modelClassName=getControllerModelClassName(controllerFactory.getWorkingModel().getControllerCtClass().getName());
		List<CtClass> modelInterfaces=getModelInterfaces(controllerFactory,ClassPool.getDefault().get(modelCtClass.getName()));
		createModelImplCtClass(controllerFactory,modelClassName, modelInterfaces,controllerHandlerCtMethod);
	}

    public CtClass makeControllerModelImpl(ControllerFactory controllerFactory,CtClass controllerCtClass,CtMethod controllerHandlerCtMethod,CtMethod controllerMethodCtMethod)throws Exception {
    	List<CtClass> modelInterfaces=getModelInterfaces(controllerFactory,controllerMethodCtMethod.getParameterTypes()[0]);
    	String modelImplName=getControllerModelClassName(controllerCtClass.getName());
    	return createModelImplCtClass(controllerFactory,modelImplName, modelInterfaces,controllerHandlerCtMethod);
    }


	public CtClass getControllerModelCtClass(String controllerClassName)throws Exception  {
		String modelClassName=getControllerModelClassName(controllerClassName);
		return ClassPool.getDefault().get(modelClassName);
	}


	public CtClass getStaticModelCtClass(String controllerClassName)throws Exception  {
		String modelClassName=getControllerModelClassName(controllerClassName);
		try{
			return ClassPool.getDefault().get(modelClassName);
    	}catch(NotFoundException ex){
    		if(!controllerClassName.equals(_controllerNameBuilder.getControllerInternalRootClassName())){
    			controllerClassName=_controllerNameBuilder.getControllerParentClassName(controllerClassName);
    			return getStaticModelCtClass(controllerClassName);
    		}else{
        		return ClassPool.getDefault().get(getProcessorHolder().getControllerBuilder().getModelSuperClass().getName());
    		}
    	}
	}

	String getControllerModelClassName(String controllerClassName) {
		return controllerClassName.toLowerCase()+".ModelImpl";
	}


    List<CtClass> getModelInterfaces(ControllerFactory controllerFactory,CtClass modelCtClass)throws Exception{
        List<CtClass> modelInterfaces=new ArrayList<CtClass>();
    	if(!modelCtClass.isInterface())
    		throw new RuntimeException("model is not an interface: "+modelCtClass.getName());
        modelInterfaces.add(modelCtClass);
        addModelIfExist(controllerFactory,modelInterfaces);
        return modelInterfaces;
    }
    
    void addModelIfExist(ControllerFactory controllerFactory,List<CtClass> modelInterfaces)throws Exception{
    	if(controllerFactory.getDynamicViewModelClass()!=null){
    		CtClass modelCtClass=ClassPool.getDefault().get(controllerFactory.getDynamicViewModelClass().getName());
    		if(!modelInterfaces.contains(modelCtClass)){
            	if(!modelCtClass.isInterface())
            		throw new RuntimeException("model is not an interface: "+modelCtClass.getName());
    			modelInterfaces.add(modelCtClass);
    		}
    	}
    	if(controllerFactory.getWorkingModel().getParentWorkModel()!=null)
    		addModelIfExist(controllerFactory.getWorkingModel().getParentWorkModel().getControllerFactory(),modelInterfaces);
    }
    


    CtClass createModelImplCtClass(ControllerFactory controllerFactory,String modelImplName,List<CtClass> modelInterfaces,CtMethod controllerMethod )throws Exception {
        CtClass modelImplCtClass = ClassPool.getDefault().makeClass(modelImplName);
        modelImplCtClass.setSuperclass(ClassPool.getDefault().get(controllerFactory.getControllerBuilder().getModelSuperClass().getName()));
        for(CtClass modelCtCass:modelInterfaces){
        	modelImplCtClass.addInterface(modelCtCass);    	
        }
        
        Map<String,Object> staticObjects=new HashMap<String,Object>();
        _modelFieldCreator.setupFields(controllerFactory,modelImplCtClass,controllerMethod,staticObjects,_modelBinder,_modelAnnotationReader,_modelMethodCreator);
        Class modelImplClass=modelImplCtClass.toClass();
        for(String fieldName:staticObjects.keySet()){
        	BeanUtils.setStaticFieldValue(modelImplClass, fieldName, staticObjects.get(fieldName));
        }
        return modelImplCtClass;
    }

	public ModelBinder getModelBinder() {
		return _modelBinder;
	}

	public void setModelBinder(ModelBinder modelBinder) {
		_modelBinder = modelBinder;
	}

	public ModelAnnotationReader getModelAnnotationReader() {
		return _modelAnnotationReader;
	}

	public void setModelAnnotationReader(ModelAnnotationReader modelAnnotationReader) {
		_modelAnnotationReader = modelAnnotationReader;
	}

	public ModelFieldCreator getModelFieldCreator() {
		return _modelFieldCreator;
	}

	public void setModelFieldCreator(ModelFieldCreator modelFieldCreator) {
		_modelFieldCreator = modelFieldCreator;
	}

	public ModelMethodCreator getModelMethodCreator() {
		return _modelMethodCreator;
	}

	public void setModelMethodCreator(ModelMethodCreator modelMethodCreator) {
		_modelMethodCreator = modelMethodCreator;
	}

	public ControllerNameBuilder getControllerNameBuilder() {
		return _controllerNameBuilder;
	}

	public void setControllerNameBuilder(ControllerNameBuilder controllerNameBuilder) {
		_controllerNameBuilder = controllerNameBuilder;
	}
}
